package com.sheng.leetcode.year2023.month08.day04;

import org.junit.Test;

/**
 * @author liusheng
 * @date 2023/08/04
 * <p>
 * 980. 不同路径 III<p>
 * <p>
 * 在二维网格 grid 上，有 4 种类型的方格：<p>
 * 1 表示起始方格。且只有一个起始方格。<p>
 * 2 表示结束方格，且只有一个结束方格。<p>
 * 0 表示我们可以走过的空方格。<p>
 * -1 表示我们无法跨越的障碍。<p>
 * 返回在四个方向（上、下、左、右）上行走时，从起始方格到结束方格的不同路径的数目。<p>
 * 每一个无障碍方格都要通过一次，但是一条路径中不能重复通过同一个方格。<p>
 * <p>
 * 示例 1：<p>
 * 输入：[[1,0,0,0],[0,0,0,0],[0,0,2,-1]]<p>
 * 输出：2<p>
 * 解释：我们有以下两条路径：<p>
 * 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)<p>
 * 2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)<p>
 * <p>
 * 示例 2：<p>
 * 输入：[[1,0,0,0],[0,0,0,0],[0,0,0,2]]<p>
 * 输出：4<p>
 * 解释：我们有以下四条路径：<p>
 * 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)<p>
 * 2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)<p>
 * 3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)<p>
 * 4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)<p>
 * <p>
 * 示例 3：<p>
 * 输入：[[0,1],[2,0]]<p>
 * 输出：0<p>
 * 解释：<p>
 * 没有一条路能完全穿过每一个空的方格一次。<p>
 * 请注意，起始和结束方格可以位于网格中的任意位置。<p>
 * <p>
 * 提示：<p>
 * 1 <= grid.length * grid[0].length <= 20<p>
 */
public class LeetCode0980 {

    @Test
    public void test01() {
//        int[][] grid = {{1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 2, -1}};
//        int[][] grid = {{1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 2}};
        int[][] grid = {{0, 1}, {2, 0}};
        System.out.println(new Solution().uniquePathsIII(grid));
    }
}

class Solution {

    // x：起点的 x 坐标；y：起点的 y 坐标；cnt：空白格数量
    int x = 0, y = 0, cnt = 0;

    // 原数组
    int[][] grid;

    // 经过的位置
    boolean[][] booleans;

    // 返回结果数
    int ans = 0;

    // 移动方向
    int[][] dir = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

    public int uniquePathsIII(int[][] grid) {
        // 找出起点 (x, y) ，并且统计空白格的数量 cnt
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] == 0) {
                    cnt++;
                } else if (grid[i][j] == 1) {
                    x = i;
                    y = j;
                }
            }
        }
        this.grid = grid;
        booleans = new boolean[grid.length][grid[0].length];
        booleans[x][y] = true;
        dfs(x, y, 0);
        return ans;
    }

    /**
     * 从 (i, j) 触发，已经走过的单元格数量为 k 的路径
     *
     * @param i x 坐标
     * @param j y 坐标
     * @param k 走过的单元格的数量
     */
    public void dfs(int i, int j, int k) {
        // 判断是否到达终点，并确认经过了所有点
        if (grid[i][j] == 2 && k == cnt + 1) {
            ans++;
            return;
        }
        for (int[] ints : dir) {
            int x = i + ints[0], y = j + ints[1];
            if (x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && !booleans[x][y] && grid[x][y] != -1) {
                // 经过 (x, y) 点
                booleans[x][y] = true;
                dfs(x, y, k + 1);
                // 回溯
                booleans[x][y] = false;
            }
        }
    }
}
